package cn.schoolwow.quickdao.module.database.parent.flow.executor;

import cn.schoolwow.quickdao.domain.QuickDAOConfig;
import cn.schoolwow.quickdao.module.database.dml.common.domain.GetBatchParametersSupplier;
import cn.schoolwow.quickdao.module.database.parent.flow.executor.common.*;
import cn.schoolwow.quickflow.domain.FlowContext;
import cn.schoolwow.quickflow.flow.BusinessFlow;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

public class ExecuteBatchUpdateConnectionFlow implements BusinessFlow {
    private Logger logger = LoggerFactory.getLogger(ExecuteBatchUpdateConnectionFlow.class);

    @Override
    public void executeBusinessFlow(FlowContext flowContext) throws Exception {
        QuickDAOConfig quickDAOConfig = flowContext.checkInstanceData(QuickDAOConfig.class);
        String name = flowContext.checkData("name", String.class);
        String sql = flowContext.checkData("sql", String.class);
        Connection transactionConnection = (Connection) flowContext.getData("transactionConnection");

        flowContext.executeFlowList(new BeforeExecuteFlow());
        logger.debug("[批处理更新]名称:{}, 执行语句:{}", name, sql);

        flowContext.startFlow(new RealtimePrintStatementCommonFlow())
                .putCurrentCompositeFlowData("sqlLogType", 1)
                .execute();

        long startTime = System.currentTimeMillis();
        try {
            if(null==transactionConnection){
                try (Connection connection = quickDAOConfig.databaseContext.dataSource.getConnection()){
                    executeBatchUpdate(connection, flowContext);
                }
            }else{
                executeBatchUpdate(transactionConnection, flowContext);
            }
        }catch (SQLException e){
            throw e;
        }finally {
            flowContext.removeData("getBatchParametersSupplier");

            long endTime = System.currentTimeMillis();
            long consumeTime = endTime-startTime;
            String content = "[更新]名称:"+name+",耗时:"+consumeTime+"ms,执行语句:"+sql+"\r\n";

            flowContext.startFlow(new RecordStatementFlow())
                    .putCurrentCompositeFlowData("content", content)
                    .execute();
        }
    }

    @Override
    public String name() {
        return "执行批处理更新语句";
    }

    private void executeBatchUpdate(Connection connection, FlowContext flowContext) throws Exception {
        QuickDAOConfig quickDAOConfig = flowContext.checkInstanceData(QuickDAOConfig.class);
        Integer perBatchCount = (Integer) flowContext.getData("perBatchCount", quickDAOConfig.databaseOption.perBatchCount);
        String sql = flowContext.checkData("sql", String.class);
        Integer size = flowContext.checkData("size", Integer.class);
        GetBatchParametersSupplier getBatchParametersSupplier = (GetBatchParametersSupplier) flowContext.checkData("getBatchParametersSupplier");

        List<Object> parameters = null;
        int effect = 0;
        flowContext.putTemporaryData("startTime", System.currentTimeMillis());
        try (PreparedStatement batchPrepareStatement = connection.prepareStatement(sql);){
            for (int i = 0; i < size; i += perBatchCount) {
                batchPrepareStatement.clearBatch();
                int end = Math.min(i + perBatchCount, size);
                for (int j = i; j < end; j++) {
                    parameters = getBatchParametersSupplier.getBatchParameters(j);
                    flowContext.startFlow(new SetFormatStatementFlow())
                            .next(new SetPrepareStatementParameterFlow())
                            .putCurrentCompositeFlowData("parameters", parameters)
                            .putCurrentCompositeFlowData("preparedStatement", batchPrepareStatement)
                            .execute();
                    batchPrepareStatement.addBatch();
                }
                effect += executeBatch(batchPrepareStatement);
            }
            flowContext.startFlow(new InsertSqlLogFlow())
                    .putTemporaryData("sqlLogType", 1)
                    .execute();
        }catch (Exception e){
            flowContext.startFlow(new InsertSqlLogFlow())
                    .next(new ExecuteFailFlow())
                    .putTemporaryData("exception", e)
                    .putTemporaryData("sqlLogType", 1)
                    .execute();
            throw e;
        }finally {
            flowContext.putData("effect", effect);
        }
    }

    private int executeBatch(PreparedStatement batchPrepareStatement) throws SQLException {
        int effect = 0;
        int[] batches = batchPrepareStatement.executeBatch();
        for (int batch : batches) {
            switch (batch) {
                case Statement.SUCCESS_NO_INFO: {
                    effect += 1;
                }
                break;
                case Statement.EXECUTE_FAILED: {
                }
                break;
                default: {
                    effect += batch;
                }
            }
        }
        return effect;
    }

}
